package cn.jaye.fabric.domain.service;

import cn.jaye.fabric.common.constant.Global;
import cn.jaye.fabric.common.constant.MicroConstant;
import cn.jaye.fabric.domain.model.ErrorRecord;
import cn.jaye.fabric.domain.model.User;
import cn.jaye.fabric.infrastructure.mybatis.impl.ErrorRecordRepositoryImpl;
import cn.jaye.fabric.infrastructure.mybatis.impl.HistoryRepositoryImpl;
import cn.jaye.fabric.infrastructure.mybatis.impl.UseRepositoryImpl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author Jaye
 * @create 2018-06-04 16:27
 */
@Service
public class BlockChainService {

    private Logger logger = LoggerFactory.getLogger(CalculateService.class);

    @Autowired
    private HistoryRepositoryImpl historyRepository;

    @Autowired
    private ErrorRecordRepositoryImpl errorRecordRepository;

    @Autowired
    private RestTemplateService restTemplateService;

    @Autowired
    private UseRepositoryImpl useRepository;

    @Autowired
    private MicroConstant microConstant;

    public void recordResult() {
        JSONArray result = historyRepository.getCount();
        if (null != result && result.size() > 0) {
            for (int i = 0; i < result.size(); i++) {
                JSONObject jsonObject = result.getJSONObject(i);
                String username = jsonObject.getString("userName");
                Integer point = jsonObject.getInteger("point");
                if (writeBlockChain(username, point)) {
                    updateHistoryState(username);
                } else {
                    ErrorRecord record = new ErrorRecord();
                    record.setUserName(username);
                    record.setScoreCount(point);
                    errorRecordRepository.insert(record);
                    logger.info("给用户计分失败");
                }
            }
        }
    }

    private boolean writeBlockChain(String username, Integer point) {
        if (point > 0) {
            return transferPoint(username, String.valueOf(point));
        }
        return true;
    }

    public boolean transferPoint(String username, String point) {
        String[] args = {microConstant.getChainSysTokenInitUser(), username, microConstant.getChainSysTokenName(), point};
        try {
            JSONObject result = invoke(Global.CHAINCODE_FUNC_TRANSFER, args);
            if (result.getInteger("code").equals(1000)) {
                logger.info("username : " + username + " has get point:" + point + " success on blockchain");
                return true;
            } else {
                logger.error("username : " + username + " has get point:" + point + " failed on blockchain");
            }
        } catch (Exception e) {
            logger.error("username : " + username + " has get point:" + point + " exception on blockchain");
        }
        return false;
    }

    public JSONObject invoke(String fcn, String[] args) throws InterruptedException {
        String url = microConstant.getChainApiAddress() + microConstant.getChainIfInvoke();
        JSONObject postData = constructParm(fcn, args);
        if (Global.CHAINCODE_FUNC_TRANSFER.equals(fcn)) {
            Thread.sleep((microConstant.getChainBatchTimeOut() + 2) * 1000);
        }
        return restTemplateService.getUrlResponse(url, postData, addUserInfoHeaders(setHeaders(new HttpHeaders())));
    }

    public JSONObject query(String username, String password) {
        String url = microConstant.getChainApiAddress() + microConstant.getChainIfQuery();
        String[] args = {username};
        JSONObject postData = constructParm(Global.CHAINCODE_FUNC_INFO, args);
        HttpHeaders httpHeaders = setHeaders(new HttpHeaders());
        httpHeaders.set("username", username);
        httpHeaders.set("password", password);
        httpHeaders.set("orgid", microConstant.getChainOrgid());
        return restTemplateService.getUrlResponse(url, postData, httpHeaders);
    }

    public JSONObject registUser(String username) {
        String url = microConstant.getChainApiAddress() + microConstant.getChainIfRegister();
        JSONObject postData = new JSONObject();
        postData.put("username", username);
        postData.put("orgId", microConstant.getChainOrgid());
        return restTemplateService.getUrlResponse(url, postData, setHeaders(new HttpHeaders()));
    }

    public JSONObject loginUser(String username, String password) {
        String url = microConstant.getChainApiAddress() + microConstant.getChainIfRegister();
        JSONObject postData = new JSONObject();
        postData.put("username", username);
        postData.put("username", password);
        postData.put("orgId", microConstant.getChainOrgid());
        return restTemplateService.getUrlResponse(url, postData, setHeaders(new HttpHeaders()));
    }


    private JSONObject constructParm(String fcn, String[] args) {
        JSONObject jsonObject = new JSONObject();
//        microConstant
        JSONObject channel = new JSONObject();
        channel.put("name", microConstant.getChannelName());
        JSONObject chaincode = new JSONObject();
        chaincode.put("name", microConstant.getChaincodeName());
        chaincode.put("version", microConstant.getChaincodeVersion());
        chaincode.put("path", microConstant.getChaincodePath());
        chaincode.put("fcn", fcn);
        chaincode.put("args", args);
        jsonObject.put("channel", channel);
        jsonObject.put("chainCode", chaincode);
        return jsonObject;
    }

    private HttpHeaders addUserInfoHeaders(HttpHeaders httpHeaders) {
        httpHeaders.set("username", microConstant.getChainUsername());
        httpHeaders.set("password", microConstant.getChainPassword());
        httpHeaders.set("orgid", microConstant.getChainOrgid());
        return httpHeaders;
    }

    private HttpHeaders setHeaders(HttpHeaders httpHeaders) {
        MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
        httpHeaders.setContentType(type);
        httpHeaders.add("Accept", MediaType.APPLICATION_JSON.toString());
        return httpHeaders;
    }


    public void checkUser() {

    }

    private void checkUserPoint(String username) {
        User user = useRepository.selectOne(new EntityWrapper<User>().eq("user_name", username));

    }

    @Transactional(rollbackFor = Exception.class)
    private void updateUserCount(String username, Integer point) {
        if (point > 0) {
            useRepository.updateUserPoint(username, point);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    private void updateHistoryState(String username) {
        historyRepository.batchUpdateState(username);
    }
}
